home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
pcl
/
sptmbr16.lha
/
lap.lisp
< prev
next >
Wrap
Lisp/Scheme
|
1992-07-24
|
16KB
|
501 lines
;;;-*-Mode: LISP; Package: PCL; Base:10; Syntax:Common-lisp -*-
;;;
;;; *************************************************************************
;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
;;; All rights reserved.
;;;
;;; Use and copying of this software and preparation of derivative works
;;; based upon this software are permitted. Any distribution of this
;;; software or derivative works must comply with all applicable United
;;; States export control laws.
;;;
;;; This software is made available AS IS, and Xerox Corporation makes no
;;; warranty about the software, its performance or its conformity to any
;;; specification.
;;;
;;; Any person obtaining a copy of this software is requested to send their
;;; name and post office or electronic mail address to:
;;; CommonLoops Coordinator
;;; Xerox PARC
;;; 3333 Coyote Hill Rd.
;;; Palo Alto, CA 94304
;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
;;;
;;; Suggestions, comments and requests for improvements are also welcome.
;;; *************************************************************************
;;;
(in-package 'pcl)
;;;
;;; This file defines PCL's interface to the LAP mechanism.
;;;
;;; The file is divided into two parts. The first part defines the interface
;;; used by PCL to create abstract LAP code vectors. PCL never creates lists
;;; that represent LAP code directly, it always calls this mechanism to do so.
;;; This provides a layer of error checking on the LAP code before it gets to
;;; the implementation-specific assembler. Note that this error checking is
;;; syntactic only, but even so is useful to have. Because of it, no specific
;;; LAP assembler should worry itself with checking the syntax of the LAP code.
;;;
;;; The second part of the file defines the LAP assemblers for each PCL port.
;;; These are included together in the same file to make it easier to change
;;; them all should some random change be made in the LAP mechanism.
;;;
(defvar *make-lap-closure-generator*)
(defvar *precompile-lap-closure-generator*)
(defvar *lap-in-lisp*)
(defun make-lap-closure-generator
(closure-variables arguments iregs vregs fvregs tregs lap-code)
(funcall *make-lap-closure-generator*
closure-variables arguments iregs
vregs fvregs tregs lap-code))
(defmacro precompile-lap-closure-generator
(cvars args i-regs v-regs fv-regs t-regs lap)
(funcall *precompile-lap-closure-generator* cvars args i-regs
v-regs fv-regs t-regs lap))
(defmacro lap-in-lisp (cvars args iregs vregs fvregs tregs lap)
(declare (ignore cvars args))
`(locally (declare #.*optimize-speed*)
,(make-lap-prog iregs vregs fvregs tregs
(flatten-lap lap (opcode :label 'exit-lap-in-lisp)))))
;;;
;;; The following functions and macros are used by PCL when generating LAP
;;; code:
;;;
;;; GENERATING-LAP
;;; WITH-LAP-REGISTERS
;;; ALLOCATE-REGISTER
;;; DEALLOCATE-REGISTER
;;; LAP-FLATTEN
;;; OPCODE
;;; OPERAND
;;;
(proclaim '(special *generating-lap*)) ;CAR - alist of free registers
;CADR - alist of allocated registers
;CADDR - max reg number allocated
;
;in each alist, the entries have
;the form: (type . (:REG <n>))
;
;;;
;;; This goes around the generation of any lap code. <body> should return a lap
;;; code sequence, this macro will take care of converting that to a lap closure
;;; generator.
;;;
(defmacro generating-lap (closure-variables arguments &body body)
`(let* ((*generating-lap* (list () () -1)))
(finalize-lap-generation nil ,closure-variables ,arguments (progn ,@body))))
(defmacro generating-lap-in-lisp (closure-variables arguments &body body)
`(let* ((*generating-lap* (list () () -1)))
(finalize-lap-generation t ,closure-variables ,arguments (progn ,@body))))
;;;
;;; Each register specification looks like:
;;;
;;; (<var> <type> &key :reuse <other-reg>)
;;;
(defmacro with-lap-registers (register-specifications &body body)
;;
;; Given that, for now, there is only one keyword argument and
;; that, for now, we do no error checking, we can be pretty
;; sleazy about how this works.
;;
(flet ((make-allocations ()
(gathering1 (collecting)
(dolist (spec register-specifications)
(gather1
`(,(car spec) (or ,(cadddr spec) (allocate-register ',(cadr spec))))))))
(make-deallocations ()
(gathering1 (collecting)
(dolist (spec register-specifications)
(gather1
`(unless ,(cadddr spec) (deallocate-register ,(car spec))))))))
`(let ,(make-allocations)
(multiple-value-prog1 (progn ,@body)
,@(make-deallocations)))))
(defun allocate-register (type)
(destructuring-bind (free allocated) *generating-lap*
(let ((entry (assoc type free)))
(cond (entry
(setf (car *generating-lap*) (delete entry free)
(cadr *generating-lap*) (cons entry allocated))
(cdr entry))
(t
(let ((new `(,type . (:reg ,(incf (caddr *generating-lap*))))))
(setf (cadr *generating-lap*) (cons new allocated))
(cdr new)))))))
(defun deallocate-register (reg)
(let ((entry (rassoc reg (cadr *generating-lap*))))
(unless entry (error "Attempt to free an unallocated register."))
(push entry (car *generating-lap*))
(setf (cadr *generating-lap*) (delete entry (cadr *generating-lap*)))))
(defvar *precompiling-lap* nil)
(defun finalize-lap-generation (in-lisp-p closure-variables arguments lap-code)
(when (cadr *generating-lap*) (error "Registers still allocated when lap being finalized."))
(let ((iregs ())
(vregs ())
(fvregs ())
(tregs ()))
(dolist (entry (car *generating-lap*))
(ecase (car entry)
(index (push (caddr entry) iregs))
(vector (push (caddr entry) vregs))
(fixnum-vector (push (caddr entry) fvregs))
((t) (push (caddr entry) tregs))))
(cond (in-lisp-p
`(lap-in-lisp ,closure-variables ,arguments ,iregs
,vregs ,fvregs ,tregs ,lap-code))
(*precompiling-lap*
`(precompile-lap-closure-generator
,closure-variables ,arguments ,iregs
,vregs ,fvregs ,tregs ,lap))
(t
(make-lap-closure-generator
closure-variables arguments iregs
vregs fvregs tregs lap-code)))))
(defun flatten-lap (&rest opcodes-or-sequences)
(let ((result ()))
(dolist (opcode-or-sequence opcodes-or-sequences result)
(cond ((null opcode-or-sequence))
((not (consp (car opcode-or-sequence))) ;its an opcode
(setf result (append result (list opcode-or-sequence))))
(t
(setf result (append result opcode-or-sequence)))))))
(defmacro flattening-lap ()
'(let ((result ()))
(values #'(lambda (value) (push value result))
#'(lambda () (apply #'flatten-lap (reverse result))))))
;;;
;;; This code deals with the syntax of the individual opcodes and operands.
;;;
;;;
;;; The first two of these variables are documented to all ports. They are
;;; lists of the symbols which name the lap opcodes and operands. They can
;;; be useful to determine whether a port has implemented all the required
;;; opcodes and operands.
;;;
;;; The third of these variables is for use of the emitter only.
;;;
(defvar *lap-operands* ())
(defvar *lap-opcodes* ())
(defvar *lap-emitters* (make-hash-table :test #'eq :size 30))
(defun opcode (name &rest args)
(let ((emitter (gethash name *lap-emitters*)))
(if emitter
(apply emitter args)
(error "No opcode named ~S." name))))
(defun operand (name &rest args)
(let ((emitter (gethash name *lap-emitters*)))
(if emitter
(apply emitter args)
(error "No operand named ~S." name))))
(defmacro defopcode (name types)
(let ((fn-name (symbol-append "LAP Opcode " name *the-pcl-package*))
(lambda-list
(mapcar #'(lambda (x) (declare (ignore x)) (gensym)) types)))
`(progn
(eval-when (load eval) (load-defopcode ',name ',fn-name))
(defun ,fn-name ,lambda-list
#+Genera (declare (sys:function-parent ,name defopcode))
(defopcode-1 ',name ',types ,@lambda-list)))))
(defmacro defoperand (name types)
(let ((fn-name (symbol-append "LAP Operand " name *the-pcl-package*))
(lambda-list
(mapcar #'(lambda (x) (declare (ignore x)) (gensym)) types)))
`(progn
(eval-when (load eval) (load-defoperand ',name ',fn-name))
(defun ,fn-name ,lambda-list
#+Genera (declare (sys:function-parent ,name defoperand))
(defoperand-1 ',name ',types ,@lambda-list)))))
(defun load-defopcode (name fn-name)
(if* (memq name *lap-operands*)
(error "LAP opcodes and operands must have disjoint names.")
(setf (gethash name *lap-emitters*) fn-name)
(pushnew name *lap-opcodes*)))
(defun load-defoperand (name fn-name)
(if* (memq name *lap-opcodes*)
(error "LAP opcodes and operands must have disjoint names.")
(setf (gethash name *lap-emitters*) fn-name)
(pushnew name *lap-operands*)))
(defun defopcode-1 (name operand-types &rest args)
(iterate ((arg (list-elements args))
(type (list-elements operand-types)))
(check-opcode-arg name arg type))
(cons name (copy-list args)))
(defun defoperand-1 (name operand-types &rest args)
(iterate ((arg (list-elements args))
(type (list-elements operand-types)))
(check-operand-arg name arg type))
(cons name (copy-list args)))
(defun check-opcode-arg (name arg type)
(labels ((usual (x)
(and (consp arg) (eq (car arg) x)))
(check (x)
(ecase x
((:reg :cdr :constant :iref :instance-ref :cvar :arg :lisp :lisp-variable)
(usual x))
(:label (symbolp arg))
(:operand (and (consp arg) (memq (car arg) *lap-operands*))))))
(unless (if (consp type)
(if (eq (car type) 'or)
(some #'check (cdr type))
(error "What type is this?"))
(check type))
(error "The argument ~S to the opcode ~A is not of type ~S." arg name type))))
(defun check-operand-arg (name arg type)
(flet ((check (x)
(ecase x
(:symbol (symbolp arg))
(:register-number (and (integerp arg) (>= arg 0)))
(:t t)
(:reg (and (consp arg) (eq (car arg) :reg)))
(:fixnum (typep arg 'fixnum)))))
(unless (if (consp type)
(if (eq (car type) 'or)
(some #'check (cdr type))
(error "What type is this?"))
(check type))
(error "The argument ~S to the operand ~A is not of type ~S." arg name type))))
;;;
;;; The actual opcodes.
;;;
(defopcode :break ()) ;For debugging only. Not
(defopcode :beep ()) ;all ports are required to
(defopcode :print (:reg)) ;implement this.
(defopcode :move (:operand (or :reg :iref :instance-ref :cdr :lisp-variable)))
(defopcode :eq ((or :reg :constant) (or :reg :constant) :label))
(defopcode :neq ((or :reg :constant) (or :reg :constant) :label))
(defopcode :fix= ((or :reg :constant) (or :reg :constant) :label))
(defopcode :izerop (:reg :label))
(defopcode :std-instance-p (:reg :label))
(defopcode :fsc-instance-p (:reg :label))
(defopcode :built-in-instance-p (:reg :label))
(defopcode :structure-instance-p (:reg :label))
(defopcode :jmp ((or :reg :constant)))
(defopcode :emf-call ((or :reg :constant)))
(defopcode :label (:label))
(defopcode :go (:label))
(defopcode :return ((or :reg :constant)))
(defopcode :exit-lap-in-lisp ())
;;;
;;; The actual operands.
;;;
(defoperand :reg (:register-number))
(defoperand :cvar (:symbol))
(defoperand :arg (:symbol))
(defoperand :cdr (:reg))
(defoperand :constant (:t))
(defoperand :std-wrapper (:reg))
(defoperand :fsc-wrapper (:reg))
(defoperand :built-in-wrapper (:reg))
(defoperand :structure-wrapper (:reg))
(defoperand :other-wrapper (:reg))
(defoperand :built-in-or-structure-wrapper (:reg))
(defoperand :std-slots (:reg))
(defoperand :fsc-slots (:reg))
(defoperand :wrapper-cache-number-vector (:reg))
(defoperand :cref (:reg :fixnum))
(defoperand :iref (:reg :reg))
(defoperand :iset (:reg :reg :reg))
(defoperand :instance-ref (:reg :reg))
(defoperand :instance-set (:reg :reg :reg))
(defoperand :i1+ (:reg))
(defoperand :i+ (:reg :reg))
(defoperand :i- (:reg :reg))
(defoperand :ilogand (:reg :reg))
(defoperand :ilogxor (:reg :reg))
(defoperand :ishift (:reg :fixnum))
(defoperand :lisp (:t))
(defoperand :lisp-variable (:symbol))
;;;
;;; LAP tests (there need to be a lot more of these)
;;;
#|
(defun make-lap-test-closure-1 (result)
#'(lambda (arg1)
(declare (pcl-fast-call))
(declare (ignore arg1))
result))
(defun make-lap-test-closure-2 (result)
#'(lambda (arg1 arg2)
(declare (pcl-fast-call))
(declare (ignore arg1 arg2))
result))
(eval-when (eval)
(compile 'make-lap-test-closure-1)
(compile 'make-lap-test-closure-2))
(proclaim '(special lap-win lap-lose))
(eval-when (load eval)
(setq lap-win (make-lap-test-closure-1 'win)
lap-lose (make-lap-test-closure-1 'lose)))
(defun lap-test-1 ()
(let* ((cg (generating-lap '(cache)
'(arg)
(with-lap-registers ((i0 index)
(v0 vector)
(t0 t))
(flatten-lap
(opcode :move (operand :cvar 'cache) v0)
(opcode :move (operand :arg 'arg) i0)
(opcode :move (operand :iref v0 i0) t0)
(opcode :jmp t0)))))
(cache (make-array 32))
(closure (funcall cg cache))
(fn0 (make-lap-test-closure-1 'fn0))
(fn1 (make-lap-test-closure-1 'fn1))
(fn2 (make-lap-test-closure-1 'fn2))
(in0 (index-value->index 2))
(in1 (index-value->index 10))
(in2 (index-value->index 27)))
(setf (svref cache (index->index-value in0)) fn0
(svref cache (index->index-value in1)) fn1
(svref cache (index->index-value in2)) fn2)
(unless (and (eq (funcall closure in0) 'fn0)
(eq (funcall closure in1) 'fn1)
(eq (funcall closure in2) 'fn2))
(error "LAP TEST 1 failed."))))
(defun lap-test-2 ()
(let* ((cg (generating-lap '(cache mask)
'(arg)
(with-lap-registers ((i0 index)
(i1 index)
(i2 index)
(v0 vector)
(t0 t))
(flatten-lap
(opcode :move (operand :cvar 'cache) v0)
(opcode :move (operand :arg 'arg) i0)
(opcode :move (operand :cvar 'mask) i1)
(opcode :move (operand :ilogand i0 i1) i2)
(opcode :move (operand :iref v0 i2) t0)
(opcode :jmp t0)))))
(cache (make-array 32))
(mask #b00110)
(closure (funcall cg cache mask))
(in0 (index-value->index #b00010))
(in1 (index-value->index #b01010))
(in2 (index-value->index #b10011)))
(fill cache lap-lose)
(setf (svref cache (index->index-value in0)) lap-win)
(unless (and (eq (funcall closure in0) 'win)
(eq (funcall closure in1) 'win)
(eq (funcall closure in2) 'win))
(error "LAP TEST 2 failed."))))
(defun lap-test-3 ()
(let* ((cg (generating-lap '(addend) '(arg)
(with-lap-registers
((i0 index)
(i1 index)
(i2 index))
(flatten-lap
(opcode :move (operand :cvar 'addend) i0)
(opcode :move (operand :arg 'arg) i1)
(opcode :move (operand :i+ i0 i1) i2)
(opcode :return i2)))))
(closure (funcall cg (index-value->index 5))))
(unless (= (index->index-value (funcall closure (index-value->index 2))) 7)
(error "LAP TEST 3 failed."))))
(defun lap-test-4 ()
(let* ((cg (generating-lap '(winner loser) '(arg)
(with-lap-registers ((t0 t))
(flatten-lap
(opcode :move (operand :arg 'arg) t0)
(opcode :eq t0 (operand :constant 'foo) 'win)
(opcode :move (operand :cvar 'loser) t0)
(opcode :jmp t0)
(opcode :label 'win)
(opcode :move (operand :cvar 'winner) t0)
(opcode :jmp t0)))))
(closure (funcall cg #'true #'false)))
(unless (and (eq (funcall closure 'foo) 't)
(eq (funcall closure 'bar) 'nil))
(error "LAP TEST 4 failed."))))
(defun lap-test-5 ()
(let* ((cg (generating-lap '(array) '(arg)
(with-lap-registers ((r0 vector)
(r1 t)
(r2 index))
(flatten-lap
(opcode :move (operand :cvar 'array) r0)
(opcode :move (operand :arg 'arg) r1)
(opcode :move (operand :constant (index-value->index 0)) r2)
(opcode :move r1 (operand :iref r0 r2))
(opcode :return r1)))))
(array (make-array 1))
(closure (funcall cg array)))
(unless (and (= (funcall closure 1) (svref array 0))
(eq (funcall closure 'foo) (svref array 0)))
(error "LAP TEST 5 failed."))))
|#